package org.example.task.filter;

import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.example.task.constant.response.HttpStatus;
import org.example.task.exception.TokenAuthExpiredException;
import org.example.task.model.po.LoginUser;
import org.example.task.service.impl.UserDetailsServiceImpl;
import org.example.task.utils.JwtUtil;
import org.example.task.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;

import java.io.IOException;
import java.util.List;
import java.util.Objects;


@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
    // 新增的排除路径配置
    private static final List<String> EXCLUDE_PATH = List.of("/user/auth/**");
    private final AntPathMatcher pathMatcher = new AntPathMatcher();
    private final JwtUtil jwtUtil;
    private final RedisUtil redisUtil;
    private final UserDetailsServiceImpl userDetailsService;

    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 1. 解析 Token
        String token = parseJwt(request);
        try {
            if (token == null) {
                // 触发 AuthenticationEntryPoint
                throw new TokenAuthExpiredException(HttpStatus.UNAUTHORIZED, "未提供有效的认证Token");
            }

            if (!jwtUtil.validateToken(token)) {
                throw new TokenAuthExpiredException(HttpStatus.UNAUTHORIZED, "Token无效或已过期");
            }

            Claims claims = jwtUtil.parseToken(token);
            String username = claims.get("username", String.class);
            String userId = claims.getSubject();
            String redisKey = "login" + userId;

            // 2. 检查 Redis 中的 Token
            String redisToken = (String) redisUtil.get(redisKey);
            if (redisToken == null) {
                throw new TokenAuthExpiredException(HttpStatus.UNAUTHORIZED, "Token已失效，请重新登录");
            }

            if (!token.equals(redisToken)) {
                throw new TokenAuthExpiredException(HttpStatus.UNAUTHORIZED, "Token不匹配，可能已在其他设备登录");
            }
            //获取用户信息
            LoginUser loginUser = redisUtil.get("auth" + userId, LoginUser.class);
            if (loginUser == null) {
                throw new TokenAuthExpiredException(HttpStatus.UNAUTHORIZED, "用户信息已过期，请重新登录");
            }
            // 4. 加载用户信息并设置认证上下文
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
            filterChain.doFilter(request, response);
        } catch (AuthenticationException e) {
            resolver.resolveException(request, response, null, e);
        }

    }

    public String parseJwt(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            return token.substring(7);
        }
        return null;
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        return EXCLUDE_PATH.stream().anyMatch(p -> pathMatcher.match(p, request.getServletPath()));
    }
}
